﻿ using UnityEngine;
 using System.Runtime.InteropServices;
public class TestCPP : MonoBehaviour
{
     struct TestStruct
     {
          public int a;
          public string b;

          public TestStruct(int _a, string _b)
          {
               a = _a;
               b = _b;
          }
     }

     private void Start()
     {
          Debug.LogError("test int ptr size: " + System.IntPtr.Size);

          Debug.LogError("test int: " + TestInt(12345678));

          System.IntPtr dupStrPtr = TestStringCopy("this is a string");
          Debug.LogError("test string copy: " + Marshal.PtrToStringUTF8(dupStrPtr));
          Free(dupStrPtr);

          byte[] data = System.Text.Encoding.Default.GetBytes("this is a data string");
          System.IntPtr outDataPtr;
          int outLen = 0;
          TestByteArrayCopy(data, data.Length, out outDataPtr, out outLen);
          byte[] outData = new byte[outLen];
          Marshal.Copy(outDataPtr, outData, 0, outLen);
          Free(outDataPtr);          
          Debug.LogError("test byte[] copy: " + System.Text.Encoding.Default.GetString(outData));



          var p = CreateTestCPP();
          int a = GetTestCPP_A(p);
          ReleaseTestCPP(p);
          p = System.IntPtr.Zero;
          Debug.LogError("test: a " + a);



          GCHandle objHandle = GCHandle.Alloc(this, GCHandleType.Weak);
          System.IntPtr address = GCHandle.ToIntPtr(objHandle);
          object obj = GCHandle.FromIntPtr(address).Target;



     
          Debug.LogError("test intptr to object: " + obj);


          var b = new byte[10];
          GCHandle arrHandle = GCHandle.Alloc(b, GCHandleType.Weak);
          System.IntPtr arrAddress = GCHandle.ToIntPtr(arrHandle);
          object arrObj = GCHandle.FromIntPtr(arrAddress).Target;
          Debug.LogError("test intptr to byte[]: " + arrObj);

          TestCallback(123, "msg text", arrAddress, CallFromCPP, address);

          objHandle.Free();
          arrHandle.Free();


          TestStruct ts = new TestStruct(2, "abc");
          System.IntPtr sp = StructToIntPtr(ts);
          System.IntPtr sp3 = StructToIntPtr(ts);
          System.IntPtr sp2 = TestObjectIntPtr(sp);
          TestStruct c =  Marshal.PtrToStructure<TestStruct>(sp2);
          Debug.LogError("test struct to intPtr: " + c.a + " | " + c.b + " | " + ts.GetHashCode() + " | " + c.GetHashCode() + " | " + sp);
          FreeStructIntPtr<TestStruct>(sp2);
    }


     [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
     public delegate void CallBackType(System.IntPtr self, int a, string msg, System.IntPtr arrPtr);
     
     [AOT.MonoPInvokeCallback(typeof(CallBackType))]
     static void CallFromCPP(System.IntPtr self, int a, string msg, System.IntPtr arrPtr)
     {
          object obj = GCHandle.FromIntPtr(self).Target;
          object arrObj = GCHandle.FromIntPtr(arrPtr).Target;
          Debug.LogError("CallFromCPP: " + self + " |" + obj + " | " + a + " | " + msg + " | " + arrObj);
     }

     private System.IntPtr StructToIntPtr<T> (T SourceContent)
     {
          int iSize = Marshal.SizeOf(typeof(T));
          System.IntPtr pTestStruct = Marshal.AllocHGlobal(iSize);
          Marshal.StructureToPtr(SourceContent, pTestStruct, false);
          return pTestStruct;
     }

     private void FreeStructIntPtr<T>(System.IntPtr p)
     {
          Marshal.DestroyStructure<T>(p);
          Marshal.FreeHGlobal(p);
     }

#if (UNITY_WEBGL || UNITY_IPHONE || UNITY_IOS) && !UNITY_EDITOR
     const string dllName = "__Internal";
#else
     const string dllName = "CPPSDK";
#endif

     [DllImport(dllName)]
     private static extern System.IntPtr CreateTestCPP();

     [DllImport(dllName)]
     private static extern void ReleaseTestCPP(System.IntPtr p);

     [DllImport(dllName)]
     private static extern int GetTestCPP_A(System.IntPtr p);

     [DllImport(dllName)]
     private static extern int TestInt(int a);
     
     [DllImport(dllName)]
     private static extern System.IntPtr TestStringCopy(string a);

     [DllImport(dllName)]
     private static extern System.IntPtr TestObjectIntPtr(System.IntPtr a);

     [DllImport(dllName)]
     private static extern void TestByteArrayCopy(byte[] data, int len, out System.IntPtr outDataPtr, out int outLen);

     [DllImport(dllName)]
     private static extern void Free(System.IntPtr p);

     [DllImport(dllName)]
     private static extern void TestCallback(int a, string msg, System.IntPtr arrPtr, CallBackType callback, System.IntPtr self);

}